Graafi objekt
Graafi objekti loomine
Graafide jaoks defineerib tidygraph pakett objekti tbl_graph, mis on sisuliselt kaks seotud tibble tüüpi objekti, üks tippude ja teine servade jaoks. Seda defineeritakse käsuga tbl_graph, millele tuleb ette anda kaks tibble või data.frame tüüpi objekti. See käsk ootab, et servade failis oleks kaks veergu: from ja to. Tipu nimed veergudes from ja to peavad olema olemas ka tippude tabelis mingi tunnusena, mille nimi antakse ette argumendiga node_key. Tuleks ka määrata, kas graaf on suunatud või suunamata, argumendiga directed.
edges_est = edges_est %>%
rename(from = ArtistName1, to = ArtistName2)
g = tbl_graph(nodes = nodes_est, edges = edges_est, node_key = "ArtistName", directed = F)
g
Graafe saab luua ka kasutades naabrusmaatrikseid, kasutades paketi igraph fuktsiooni graph.adjacency ja muutes selle tbl_graph objektiks funktsiooniga as_tbl_graph.
library(igraph)
mat = cbind(c(0, 0, 1), c(1, 0, 1), c(1, 0, 0))
rownames(mat) = c("A", "B", "C")
colnames(mat) = c("A", "B", "C")
mat
A B C
A 0 1 1
B 0 0 0
C 1 1 0
graph.adjacency(mat) %>%
as_tbl_graph()
# A tbl_graph: 3 nodes and 4 edges
#
# A directed simple graph with 1 component
#
# Node Data: 3 x 1 (active)
name
<chr>
1 A
2 B
3 C
#
# Edge Data: 4 x 2
from to
<int> <int>
1 1 2
2 1 3
3 3 1
# … with 1 more row
Graafide manipuleerimine
Graafi objekti manipuleerimine tidygraph paketis on väga sarnane tavaliste andmetabelite töötlemisega tidyverse abil. Kuna aga graafi objekt koosneb kahest andmetabelist, siis on lisatud käsk nimega activate, millega saab ette öelda graaf, kas me plaanime rakendada käske tippude või servade tabelile. Vastavalt siis activate(nodes) ja activate(edges) aktiveerivad vastavalt tipud ja servad. Peale seda saame rakendada tuttavaid käske nagu näiteks mutate.
g %>%
activate(edges) %>%
mutate(VeryPopular = maxPopularity > 40)
g %>%
activate(nodes) %>%
mutate(VeryPopular = ArtistPopularity > 45)
Kui filtreerida tippusid, siis on sellel tagajärjed ka servadele. Nimelt kustutatakse ära ka servad, mille puhul vähemalt üks tipp andemtest välja visatakse. See on mugav, sest nii ei pea muretsema, et kaks tabelit sünkroonist välja lähevad.
g %>%
activate(nodes) %>%
mutate(VeryPopular = ArtistPopularity > 45) %>%
filter(VeryPopular == T)
Erinevate tidyverse funktsioonide kasutamisel on loomulikult piirid. Näiteks summarize on käsk mille käitumist graafil ei ole võimalik üksühele üle kanda ja seega see antud juhul ei tööta. Küll aga töötavad kõik *_join funktsioonid ja on defineeritud graafide liitmine bind_graph abil. Loe lähemalt lingilt https://www.data-imaginist.com/2017/introducing-tidygraph/.
Ülesanded
Loome kaks tabelit
n = tibble(
Name = c("A", "B", "C", "D"),
Value = 1:4,
Class = c("Class1", "Class2", "Class2", "Class2")
)
e = tibble(
Name1 = c("A", "A", "B", "C", "C", "D"),
Name2 = c("C", "D", "A", "A", "B", "B")
)
e <- e %>%
rename(from = Name1, to = Name2)
tbl_graph$nodes(nodes =e, node_key = )
Nende tabelite põhjal looge tbl_graph tüüpi objekt. Mõelge, kas antud graaf peaks olema suunatud või suunamata
Saadud graafist filtreerige välja alamgraaf mis sisaldab ainult klassi “Class2” kuuluvaid tippe.
Graafi joonistamine
Graafe joonistame selles praktikumis paketiga ggraph, mis on sisliselt laiendus ggplot2-le graafi andmete jaoks. Graafik luukase siin käsuga ggraph millele hakatakse siis graafiku elemente liitma. Tippude joonistamiseks on funktsioonid geom_node_* ja servade jaoks geom_edge_*. Andmepunktide kujutused graafilistele parameetritele antekse ette geom_ funktsioonide sees, sest me võime tahta sama parameetrit (näit. värv) defineerida nii tippudele kui servadele.
ggraph(g) +
geom_node_point() +
geom_edge_link()
Antud graaf ei näe väga hea välja, sest automaatselt valitud punktide paigutuse algoritm (stress), ei ole optimaalne. Punktide paigutust võib kontrollida parameetriga layout.
Esiteks võib sellele ette anda ühe mitmest paigutus algoritmist, millest mõned on järgnevad:
“kk” - Kamada and Kawai jõududel põhinev algoritm
“fr” - Fruchterman and Reingold jõududel põhinev algoritm
“drl” - järjekordne jõududel põhinev algoritm
“gem” - järjekordne jõududel põhinev algoritm
“stress” - järjekordne jõududel põhinev algoritm
“lgl” - suurtele graafidele sobiv algoritm
“mds” - kauguste maatrikil dimensiini vähendamiega paigutamine
“dh” - stohhastilisel lokaalsel otsingul põhinev algoritm
“nicely” - proovib valida andmetele vastaval hea algoritmi
“circle” - ringi kujuline paigutus

Paigutuse võib hea tahtmise korral ka ise ette anda maatriksina.
g
# A tbl_graph: 87 nodes and 74 edges
#
# An undirected simple graph with 34 components
#
# Node Data: 87 x 4 (active)
ArtistName ArtistPopularity ArtistFollowers ArtistGenre
<chr> <dbl> <dbl> <chr>
1 Pluuto 42 4231 "estonian hip ho…
2 Nublu 55 37366 "estonian pop"
3 5MIINUST 45 21454 "estonian hip ho…
4 PLUUTO 25 71 ""
5 Reket 48 13520 "estonian hip ho…
6 NOËP 53 23581 "estonian electr…
# … with 81 more rows
#
# Edge Data: 74 x 4
from to nTracks maxPopularity
<int> <int> <int> <dbl>
1 23 24 1 34
2 2 23 1 34
3 3 58 1 44
# … with 71 more rows
lo = cbind(runif(87), runif(87))
lo
[,1] [,2]
[1,] 0.662596733 0.075323027
[2,] 0.269026553 0.889997697
[3,] 0.135550009 0.029140577
[4,] 0.169536707 0.507268359
[5,] 0.302969098 0.910015675
[6,] 0.936581166 0.147695661
[7,] 0.693252315 0.607692787
[8,] 0.845766872 0.675253721
[9,] 0.880355598 0.987907342
[10,] 0.029445802 0.368267840
[11,] 0.222628514 0.102911692
[12,] 0.326749816 0.664828027
[13,] 0.009221235 0.447287791
[14,] 0.221918406 0.777034756
[15,] 0.236033614 0.625638479
[16,] 0.877495246 0.927023692
[17,] 0.632426293 0.401834335
[18,] 0.546805055 0.408971063
[19,] 0.411800624 0.607091832
[20,] 0.770410382 0.964285584
[21,] 0.379112357 0.090026133
[22,] 0.546063718 0.107978636
[23,] 0.897821723 0.730566913
[24,] 0.610101395 0.662014036
[25,] 0.351345219 0.975902406
[26,] 0.056662203 0.140464226
[27,] 0.214647673 0.217428566
[28,] 0.022600331 0.571534687
[29,] 0.298666016 0.155996276
[30,] 0.381898565 0.130839787
[31,] 0.638748182 0.524384490
[32,] 0.879089025 0.352608104
[33,] 0.173024775 0.921068273
[34,] 0.898233602 0.923170586
[35,] 0.233122667 0.150494365
[36,] 0.686841902 0.446558392
[37,] 0.981642799 0.758673116
[38,] 0.736455046 0.188965944
[39,] 0.398220531 0.003930629
[40,] 0.919550775 0.290675855
[41,] 0.745733682 0.458774625
[42,] 0.993803212 0.680372039
[43,] 0.704944525 0.612356485
[44,] 0.107252625 0.147779734
[45,] 0.337077885 0.733460389
[46,] 0.615534944 0.626856267
[47,] 0.704329374 0.252769461
[48,] 0.597881675 0.687044830
[49,] 0.229773201 0.644919660
[50,] 0.334824573 0.295184639
[51,] 0.558033166 0.920886023
[52,] 0.519211445 0.876314067
[53,] 0.778482103 0.509269712
[54,] 0.038757446 0.303369572
[55,] 0.650278749 0.747693845
[56,] 0.967808788 0.767233180
[57,] 0.427266140 0.419479232
[58,] 0.851476549 0.829745990
[59,] 0.359234563 0.193245298
[60,] 0.200083146 0.493635337
[61,] 0.354128396 0.410249556
[62,] 0.487300077 0.952217243
[63,] 0.415436797 0.435679120
[64,] 0.624097493 0.108958049
[65,] 0.887584910 0.830717738
[66,] 0.703132574 0.866754119
[67,] 0.735427797 0.755324924
[68,] 0.481408453 0.976971670
[69,] 0.023100206 0.945993355
[70,] 0.776635162 0.965128212
[71,] 0.822792752 0.111939796
[72,] 0.827775015 0.537961433
[73,] 0.340784257 0.639176049
[74,] 0.400972096 0.482579273
[75,] 0.647671234 0.976453587
[76,] 0.674764991 0.923791490
[77,] 0.282198098 0.505036931
[78,] 0.530972903 0.330972680
[79,] 0.549650805 0.694506390
[80,] 0.878241215 0.184076820
[81,] 0.468372708 0.726386093
[82,] 0.075580038 0.654911363
[83,] 0.518065521 0.587482974
[84,] 0.446811819 0.729395255
[85,] 0.531445435 0.857632499
[86,] 0.209030181 0.568405746
[87,] 0.873584309 0.632924577
ggraph(g, layout = lo) +
geom_node_point() +
geom_edge_link()

Tippude näitamisel, peale punktide väga häid võimalusi pole. Küll aga on erinevaid võimalusi servade joonistamiseks. Kõige kasutatavam on juba eelpool kasutatud geom_edge_link, mis tõmbab tippude vahele sirged jooned. Teised võimalused on veel:
geom_edge_arc - teeb kaare punktide vahele
geom_edge_density - joone asemel teeb kahe punkti vahelist ala natuke tumedamaks, kasulik, kui graaf on väga suur ja tihe

Kui graafid on suunatud saab joontele lisada ka nooli, mida saab spetsifitseerida argumendiga arrow, mis omamkorda võtab väärtuseks arrow funktsiooni grid paketist.

Tekstide lisamine graafile
Siiamaani oleme joonistanud küll punktikesi, kuid nende punktide tegelikku identiteeti võime vaid aimata. Et paremini teada saada, mis tippudega on täpsemalt tegu. saame lisada tippudele nimed. Seda saab teha nii funktsiooniga geom_node_text, kui geom_node_label, mis vastavalt joonistavad lihtsalt teksti või siis kastikesega sildi.

Nüüd on aga probleem, et me ei näe enam tippusid , sest need on tekstide taga peidus. Üleüldse on tekste nii palju, et nad ei mahu hästi ekraanile. Kasulik oleks neid kuidagi vähendada. Üks võimalus on näidata ainult mingis mõttes huvitavamaid tekste, näiteks neid artiste kellel on palju jälgijaid. Selleks saame kasutada geom_* argumenti data, milllele me saame anda ette funktsiooni mis modifitseerib selle kihi andmeid.

Pane tähele, et see funktsioon töötab ainult tippude andmetabelil, sest see on see mille geom_node_label ette saab. Et trükkimise vaeva vähendada saab function(.x){.x %>% filter(ArtistFollowers > 10000)} väljendada lühemalt kui ~ .x %>% filter(ArtistFollowers > 10000). Sellist kuju kasutatakse palju ka tidyverse paketis purrr, mis on mõeldud vektorite ja listide töötlemiseks.
ggraph(g, layout = "fr") +
geom_edge_link(aes(width = maxPopularity), color = "grey60") +
geom_node_point(aes(size = ArtistFollowers), color = "darkgreen") +
scale_edge_width(range = c(0, 2)) +
geom_node_label(aes(label = ArtistName), data = ~ .x %>% filter(ArtistFollowers > 10000))
Siiski on konkreetsed tipud siltide varjus ja kui silte oleks rohkem, siis kattuksid need ka üle. Et proovida leida siltidele head mitte-ülekattuvad positsioonid võib rakendada argumenti repel, mis proovib optimeerida siltide asukohti.

Lõpuks vaatame graafi kujunduselementide muutmist. Siin töötavad tegelikult kõik ggplot2 theme_*funktsioonid aga kuna neil on suur rühk telgede kujutamisel ning kirjeldamisel, siis graafide jaoks nad liiga head ei ole. Parem on kasutada theme_void või theme_graph. Konkreetsete elementide muutmiseks saab kasutada theme funktsiooni.

Ülesanded
- Proovi saavutada järgmine pilt.


Graafi algoritmid
Graafi struktuurist on võimalik järeldada päris palju huvitavat metainfot, selle info kasutamine võimaldab ka graafe visuaalselt sisukamalt kujutada. Selleks tuleb aga teada kuidas erinevaid graafi algoritme praktikas rakendada.
Kõige lihtsam omadus mille me graaf struktuurist välja saame lugeda on see, kas tipp on ühendatud teiste tippudega või mitte. Selleks on funktsioon node_is_isolated mida saab siis rakendada käskude mutate või filter sees. Pane tähele, et selle käsu töötamiseks peavad olema aktiveeritud tipud. Tippudel on ka palju teisi omadusi mida saab täpsemalt uurida abifailidest ?node_is_isolated.

Järgmisen vaatame tippude seoseid konkreetsete tippudega. Kaugust tippudega saab leida käsuga node_distance_to ja node_distance_from. Suunamata graafi puhul pole suurt vahet kumba kasutada, kuid suunatud graafi puhul tuleb erinevus sisse. Nende käskudega on väga hea eraldada välja alamgraafe, mis keskenduvad ühe punkti ümbrusele. On ka keerukamaid tippude vahelisi suhteid võimalik arvutada ja neid näeb abilehel ?node_distance_to.

Tipu olulisust graafis võib näidata mitut moodi, kuid kõiki neid lähenemisi võetakse kokku ühe terminiga tsentraalsus. Kõig lihtsam tsentraalsuse mõõt, tipu valents, loeb kokku kõik tipu ühendused. Suunatud graafis võib seda teha nii sisse kui väljapoole ühendustega eraldi. R-s saab seda arvutada käsuga centrality_degree. Keerukam tsentraalsusmõõt on betweenness centrality, mis müüdab kui suur osa ühendustest graafi tippude vahel läbib vastavat tippu. Erinevaid algoritme on aga veel (vt ?centrality_degree)
g %>%
activate(nodes) %>%
mutate(Degree = centrality_degree()) %>%
mutate(Centrality = centrality_degree()) %>%
ggraph(layout = "kk") +
geom_edge_link() +
geom_node_point(aes(size = Degree, color = Centrality, alpha = betweenness)) +
geom_node_label(aes(label = ArtistName), data = ~ .x %>% filter(Centrality > 10))
Error: Aesthetics must be valid data columns. Problematic aesthetic(s): alpha = betweenness.
Did you mistype the name of a data column or forget to add after_stat()?
Run `rlang::last_error()` to see where the error occurred.

Omadusi võib vaadata ka servadel. Ka nende puhul saab leida servad, mis üheduvad konkreetse tipuga või servasid mis on võrgustikus kesksel kohal. Neid funktsioone saab kasutada käskudega edge_is_from ja centrality_edge_betweenness.

Greefi tippe on ka kasulik klasterdada, et üles leida loogilised alamgraafid. Lihtsaim klasterdamise viis on leida sidusad alamgraafid, mida saab teha käsuga group_components. Keerukamaid kalsterdusi on ka päris palju saadaval ja ühe sellise näiteks võiks olla group_louvain (teiste jaoks uuri ?group_louvain).
g %>%
activate(nodes) %>%
filter(!node_is_isolated()) %>%
mutate(GL = as.factor(group_louvain())) %>%
ggraph(layout = "kk") +
geom_edge_link() +
geom_node_point(aes(color = GL))+
geom_node_label(aes(label = ArtistName), data = ~ .x %>% filter(Centrality > 3))
Error: Problem with `filter()` input `..1`.
x object 'Centrality' not found
ℹ Input `..1` is `Centrality > 3`.
Run `rlang::last_error()` to see where the error occurred.

Ülesanded
- Visualiseeri antud andmestiku kõige suurem sidus komponent. Näita nimedega kõik artistid, kellel on rohkem kui 3 koostööd. Näita punkti tüübiga ära selle selles graafis olevad klastirid (Louvain-i algoritmi järgi)

LS0tCnRpdGxlOiAiUHJha3Rpa3VtIDYgLSBHcmFhZmlkIGphIHbDtXJndXN0aWt1ZCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgU2lzc2VqdWhhdHVzCgpUw6RuYXNlIHByYWt0aWt1bWkgdGVlbWEgb24gZ3JhYWZpZGUgamEgdsO1cmd1c3Rpa2UgdmlzdWFsaXNlZXJpbWluZS4gU2VkYSB0ZWVtZSBtZSBrYXN1dGFkZXMgcGFrZXR0ZSBgdGlkeWdyYXBoYCBqYSBgZ2dyYXBoYC4gTGlzYWtzIGthc3V0YW1hZSBhbmRtZXN0aWtrdSwga3VzIG9uIHbDtWV0dWQgdmlpbWFzZSBwb29sZXRlaXNlIGFhc3RhIHBvcHVsYWFyc2VtYWQgRWVzdGkgbG9vZCBTcG90aWZ5c3QgamEgbmVuZGUgZXNpdGFqYXRlIGtvaHRhIHRla2l0YXR1ZCB2w7VyZ3VzdGlrIHZhc3RhdmFsdCBzZWxsZWxlIGtlcyBvbiBvbWF2YWhlbCBrb29zdMO2w7ZkIHRlaW51ZC4gSWdhIGFydGlzdGkga29odGEgb24gdGVhZGEgdGVtYSBwb3B1bGFhcnN1cyAoc2thYWxhcyAwLTEwMCkgamEgasOkbGdpamF0ZSBhcnYuIElnYSBzZXJ2YSBwdWh1bCBvbiBrb2trdSBsb2V0dWQga29vc3TDtsO2cyB2YWxtaW51ZCBsdWd1ZGUgYXJ2IGphIGvDtWlnZSBwb3B1bGFhcnNlbWEgbG9vIHBvcHVsYWFyc3VzLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpZHlncmFwaCkKbGlicmFyeShnZ3JhcGgpCgpsb2FkKCJzcG90aWZ5X2VzdC5SRGF0YSIsIHZlcmJvc2UgPSBUKQpub2Rlc19lc3QKZWRnZXNfZXN0CmBgYAoKIyMgR3JhYWZpIG9iamVrdAoKIyMjIEdyYWFmaSBvYmpla3RpIGxvb21pbmUgCgpHcmFhZmlkZSBqYW9rcyBkZWZpbmVlcmliIGB0aWR5Z3JhcGhgIHBha2V0dCBvYmpla3RpIGB0YmxfZ3JhcGhgLCBtaXMgb24gc2lzdWxpc2VsdCBrYWtzIHNlb3R1ZCBgdGliYmxlYCB0w7zDvHBpIG9iamVrdGksIMO8a3MgdGlwcHVkZSBqYSB0ZWluZSBzZXJ2YWRlIGphb2tzLiBTZWRhIGRlZmluZWVyaXRha3NlIGvDpHN1Z2EgYHRibF9ncmFwaGAsIG1pbGxlbGUgdHVsZWIgZXR0ZSBhbmRhIGtha3MgYHRpYmJsZWAgdsO1aSBgZGF0YS5mcmFtZWAgdMO8w7xwaSBvYmpla3RpLiBTZWUga8Okc2sgb290YWIsIGV0IHNlcnZhZGUgZmFpbGlzIG9sZWtzIGtha3MgdmVlcmd1OiBgZnJvbWAgamEgYHRvYC4gVGlwdSBuaW1lZCB2ZWVyZ3VkZXMgYGZyb21gIGphIGB0b2AgcGVhdmFkIG9sZW1hIG9sZW1hcyBrYSB0aXBwdWRlIHRhYmVsaXMgbWluZ2kgdHVubnVzZW5hLCBtaWxsZSBuaW1pIGFudGFrc2UgZXR0ZSBhcmd1bWVuZGlnYSBgbm9kZV9rZXlgLiBUdWxla3Mga2EgbcOkw6RyYXRhLCBrYXMgZ3JhYWYgb24gc3V1bmF0dWQgdsO1aSBzdXVuYW1hdGEsIGFyZ3VtZW5kaWdhIGBkaXJlY3RlZGAuCgpgYGB7cn0KZWRnZXNfZXN0ID0gZWRnZXNfZXN0ICU+JSAKICByZW5hbWUoZnJvbSA9IEFydGlzdE5hbWUxLCB0byA9IEFydGlzdE5hbWUyKQoKZyA9IHRibF9ncmFwaChub2RlcyA9IG5vZGVzX2VzdCwgZWRnZXMgPSBlZGdlc19lc3QsIG5vZGVfa2V5ID0gIkFydGlzdE5hbWUiLCBkaXJlY3RlZCA9IEYpCmcgCmBgYAoKR3JhYWZlIHNhYWIgbHV1YSBrYSBrYXN1dGFkZXMgbmFhYnJ1c21hYXRyaWtzZWlkLCBrYXN1dGFkZXMgcGFrZXRpIGBpZ3JhcGhgIGZ1a3RzaW9vbmkgYGdyYXBoLmFkamFjZW5jeWAgamEgbXV1dGVzIHNlbGxlIGB0YmxfZ3JhcGhgIG9iamVrdGlrcyBmdW5rdHNpb29uaWdhIGBhc190YmxfZ3JhcGhgLgoKYGBge3J9CmxpYnJhcnkoaWdyYXBoKQoKbWF0ID0gY2JpbmQoYygwLCAwLCAxKSwgYygxLCAwLCAxKSwgYygxLCAwLCAwKSkKcm93bmFtZXMobWF0KSA9IGMoIkEiLCAiQiIsICJDIikKY29sbmFtZXMobWF0KSA9IGMoIkEiLCAiQiIsICJDIikKbWF0CgpncmFwaC5hZGphY2VuY3kobWF0KSAlPiUgCiAgYXNfdGJsX2dyYXBoKCkKYGBgCgojIyMgR3JhYWZpZGUgbWFuaXB1bGVlcmltaW5lCgpHcmFhZmkgb2JqZWt0aSBtYW5pcHVsZWVyaW1pbmUgdGlkeWdyYXBoIHBha2V0aXMgb24gdsOkZ2Egc2FybmFuZSB0YXZhbGlzdGUgYW5kbWV0YWJlbGl0ZSB0w7bDtnRsZW1pc2VnYSBgdGlkeXZlcnNlYCBhYmlsLiBLdW5hIGFnYSBncmFhZmkgb2JqZWt0IGtvb3NuZWIga2FoZXN0IGFuZG1ldGFiZWxpc3QsIHNpaXMgb24gbGlzYXR1ZCBrw6RzayBuaW1lZ2EgYGFjdGl2YXRlYCwgbWlsbGVnYSBzYWFiIGV0dGUgw7ZlbGRhIGdyYWFmLCBrYXMgbWUgcGxhYW5pbWUgcmFrZW5kYWRhIGvDpHNrZSB0aXBwdWRlIHbDtWkgc2VydmFkZSB0YWJlbGlsZS4gVmFzdGF2YWx0IHNpaXMgYGFjdGl2YXRlKG5vZGVzKWAgamEgYGFjdGl2YXRlKGVkZ2VzKWAgYWt0aXZlZXJpdmFkIHZhc3RhdmFsdCB0aXB1ZCBqYSBzZXJ2YWQuIFBlYWxlIHNlZGEgc2FhbWUgcmFrZW5kYWRhIHR1dHRhdmFpZCBrw6Rza2UgbmFndSBuw6RpdGVrcyBgbXV0YXRlYC4KCmBgYHtyfQpnICU+JSAKICBhY3RpdmF0ZShlZGdlcykgJT4lIAogIG11dGF0ZShWZXJ5UG9wdWxhciA9IG1heFBvcHVsYXJpdHkgPiA0MCkKCmcgJT4lIAogIGFjdGl2YXRlKG5vZGVzKSAlPiUgCiAgbXV0YXRlKFZlcnlQb3B1bGFyID0gQXJ0aXN0UG9wdWxhcml0eSA+IDQ1KQpgYGAKCkt1aSBmaWx0cmVlcmlkYSB0aXBwdXNpZCwgc2lpcyBvbiBzZWxsZWwgdGFnYWrDpHJqZWQga2Egc2VydmFkZWxlLiBOaW1lbHQga3VzdHV0YXRha3NlIMOkcmEga2Egc2VydmFkLCBtaWxsZSBwdWh1bCB2w6RoZW1hbHQgw7xrcyB0aXBwIGFuZGVtdGVzdCB2w6RsamEgdmlzYXRha3NlLiBTZWUgb24gbXVnYXYsIHNlc3QgbmlpIGVpIHBlYSBtdXJldHNlbWEsIGV0IGtha3MgdGFiZWxpdCBzw7xua3Jvb25pc3QgdsOkbGphIGzDpGhldmFkLgoKYGBge3J9CmcgJT4lIAogIGFjdGl2YXRlKG5vZGVzKSAlPiUgCiAgbXV0YXRlKFZlcnlQb3B1bGFyID0gQXJ0aXN0UG9wdWxhcml0eSA+IDQ1KSAlPiUKICBmaWx0ZXIoVmVyeVBvcHVsYXIgPT0gVCkKYGBgCgpFcmluZXZhdGUgYHRpZHl2ZXJzZWAgZnVua3RzaW9vbmlkZSBrYXN1dGFtaXNlbCBvbiBsb29tdWxpa3VsdCBwaWlyaWQuIE7DpGl0ZWtzIHN1bW1hcml6ZSBvbiBrw6RzayBtaWxsZSBrw6RpdHVtaXN0IGdyYWFmaWwgZWkgb2xlIHbDtWltYWxpayDDvGtzw7xoZWxlIMO8bGUga2FuZGEgamEgc2VlZ2Egc2VlIGFudHVkIGp1aHVsIGVpIHTDtsO2dGEuIEvDvGxsIGFnYSB0w7bDtnRhdmFkIGvDtWlrIGAqX2pvaW5gIGZ1bmt0c2lvb25pZCBqYSBvbiBkZWZpbmVlcml0dWQgZ3JhYWZpZGUgbGlpdG1pbmUgYGJpbmRfZ3JhcGhgIGFiaWwuIExvZSBsw6RoZW1hbHQgbGluZ2lsdCA8aHR0cHM6Ly93d3cuZGF0YS1pbWFnaW5pc3QuY29tLzIwMTcvaW50cm9kdWNpbmctdGlkeWdyYXBoLz4uCgojIyMjIMOcbGVzYW5kZWQKCkxvb21lIGtha3MgdGFiZWxpdAoKYGBge3J9Cm4gPSB0aWJibGUoCiAgTmFtZSA9IGMoIkEiLCAiQiIsICJDIiwgIkQiKSwKICBWYWx1ZSA9IDE6NCwKICBDbGFzcyA9IGMoIkNsYXNzMSIsICJDbGFzczIiLCAiQ2xhc3MyIiwgIkNsYXNzMiIpCikKCmUgPSB0aWJibGUoCiAgTmFtZTEgPSBjKCJBIiwgIkEiLCAiQiIsICJDIiwgIkMiLCAiRCIpLAogIE5hbWUyID0gYygiQyIsICJEIiwgIkEiLCAiQSIsICJCIiwgIkIiKQopCmBgYAoKYGBge3J9CmUgPC0gZSAlPiUgCiAgcmVuYW1lKGZyb20gPSBOYW1lMSwgdG8gPSBOYW1lMikKCnRibF9ncmFwaCRub2Rlcyhub2RlcyA9ZSwgbm9kZV9rZXkgPSApCmBgYAoKCi0gICBOZW5kZSB0YWJlbGl0ZSBww7VoamFsIGxvb2dlIGB0YmxfZ3JhcGhgIHTDvMO8cGkgb2JqZWt0LiBNw7VlbGdlLCBrYXMgYW50dWQgZ3JhYWYgcGVha3Mgb2xlbWEgc3V1bmF0dWQgdsO1aSBzdXVuYW1hdGEKCi0gICBTYWFkdWQgZ3JhYWZpc3QgZmlsdHJlZXJpZ2UgdsOkbGphIGFsYW1ncmFhZiBtaXMgc2lzYWxkYWIgYWludWx0IGtsYXNzaSAiQ2xhc3MyIiBrdXVsdXZhaWQgdGlwcGUuCgojIyBHcmFhZmkgam9vbmlzdGFtaW5lIAoKR3JhYWZlIGpvb25pc3RhbWUgc2VsbGVzIHByYWt0aWt1bWlzIHBha2V0aWdhIGBnZ3JhcGhgLCBtaXMgb24gc2lzbGlzZWx0IGxhaWVuZHVzIGBnZ3Bsb3QyYC1sZSBncmFhZmkgYW5kbWV0ZSBqYW9rcy4gR3JhYWZpayBsdXVrYXNlIHNpaW4ga8Okc3VnYSBgZ2dyYXBoYCBtaWxsZWxlIGhha2F0YWtzZSBzaWlzIGdyYWFmaWt1IGVsZW1lbnRlIGxpaXRtYS4gVGlwcHVkZSBqb29uaXN0YW1pc2VrcyBvbiBmdW5rdHNpb29uaWQgYGdlb21fbm9kZV8qYCBqYSBzZXJ2YWRlIGphb2tzIGBnZW9tX2VkZ2VfKmAuIEFuZG1lcHVua3RpZGUga3VqdXR1c2VkIGdyYWFmaWxpc3RlbGUgcGFyYW1lZXRyaXRlbGUgYW50ZWtzZSBldHRlIGBnZW9tX2AgZnVua3RzaW9vbmlkZSBzZWVzLCBzZXN0IG1lIHbDtWltZSB0YWh0YSBzYW1hIHBhcmFtZWV0cml0IChuw6RpdC4gdsOkcnYpIGRlZmluZWVyaWRhIG5paSB0aXBwdWRlbGUga3VpIHNlcnZhZGVsZS4KCmBgYHtyfQpnZ3JhcGgoZykgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICBnZW9tX2VkZ2VfbGluaygpCmBgYAoKQW50dWQgZ3JhYWYgZWkgbsOkZSB2w6RnYSBoZWEgdsOkbGphLCBzZXN0IGF1dG9tYWF0c2VsdCB2YWxpdHVkIHB1bmt0aWRlIHBhaWd1dHVzZSBhbGdvcml0bSAoc3RyZXNzKSwgZWkgb2xlIG9wdGltYWFsbmUuIFB1bmt0aWRlIHBhaWd1dHVzdCB2w7VpYiBrb250cm9sbGlkYSBwYXJhbWVldHJpZ2EgYGxheW91dGAuCgpFc2l0ZWtzIHbDtWliIHNlbGxlbGUgZXR0ZSBhbmRhIMO8aGUgbWl0bWVzdCBwYWlndXR1cyBhbGdvcml0bWlzdCwgbWlsbGVzdCBtw7VuZWQgb24gasOkcmduZXZhZDoKCi0gICAia2siIC0gS2FtYWRhIGFuZCBLYXdhaSBqw7V1ZHVkZWwgcMO1aGluZXYgYWxnb3JpdG0KCi0gICAiZnIiIC0gRnJ1Y2h0ZXJtYW4gYW5kIFJlaW5nb2xkIGrDtXVkdWRlbCBww7VoaW5ldiBhbGdvcml0bQoKLSAgICJkcmwiIC0gasOkcmpla29yZG5lIGrDtXVkdWRlbCBww7VoaW5ldiBhbGdvcml0bQoKLSAgICJnZW0iIC0gasOkcmpla29yZG5lIGrDtXVkdWRlbCBww7VoaW5ldiBhbGdvcml0bQoKLSAgICJzdHJlc3MiIC0gasOkcmpla29yZG5lIGrDtXVkdWRlbCBww7VoaW5ldiBhbGdvcml0bQoKLSAgICJsZ2wiIC0gc3V1cnRlbGUgZ3JhYWZpZGVsZSBzb2JpdiBhbGdvcml0bQoKLSAgICJtZHMiIC0ga2F1Z3VzdGUgbWFhdHJpa2lsIGRpbWVuc2lpbmkgdsOkaGVuZGFtaWVnYSBwYWlndXRhbWluZQoKLSAgICJkaCIgLSBzdG9oaGFzdGlsaXNlbCBsb2thYWxzZWwgb3RzaW5ndWwgcMO1aGluZXYgYWxnb3JpdG0KCi0gICAibmljZWx5IiAtIHByb292aWIgdmFsaWRhIGFuZG1ldGVsZSB2YXN0YXZhbCBoZWEgYWxnb3JpdG1pCgotICAgImNpcmNsZSIgLSByaW5naSBrdWp1bGluZSBwYWlndXR1cwoKYGBge3J9CmdncmFwaChnLCBsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9ub2RlX3BvaW50KCkgKwogIGdlb21fZWRnZV9saW5rKCkKYGBgCgpQYWlndXR1c2UgdsO1aWIgaGVhIHRhaHRtaXNlIGtvcnJhbCBrYSBpc2UgZXR0ZSBhbmRhIG1hYXRyaWtzaW5hLgoKYGBge3J9CmcgIAoKI2xvb21lIGlzZSBqdWh1c2xpa3UgcGFpZ3V0dXNlLiBFaSBvbGUgaGVhCmxvID0gY2JpbmQocnVuaWYoODcpLCBydW5pZig4NykpCgpsbwoKZ2dyYXBoKGcsIGxheW91dCA9IGxvKSArCiAgZ2VvbV9ub2RlX3BvaW50KCkgKwogIGdlb21fZWRnZV9saW5rKCkKYGBgCgpUaXBwdWRlIG7DpGl0YW1pc2VsLCBwZWFsZSBwdW5rdGlkZSB2w6RnYSBow6RpZCB2w7VpbWFsdXNpIHBvbGUuIEvDvGxsIGFnYSBvbiBlcmluZXZhaWQgdsO1aW1hbHVzaSBzZXJ2YWRlIGpvb25pc3RhbWlzZWtzLiBLw7VpZ2Uga2FzdXRhdGF2YW0gb24ganViYSBlZWxwb29sIGthc3V0YXR1ZCBgZ2VvbV9lZGdlX2xpbmtgLCBtaXMgdMO1bWJhYiB0aXBwdWRlIHZhaGVsZSBzaXJnZWQgam9vbmVkLiBUZWlzZWQgdsO1aW1hbHVzZWQgb24gdmVlbDoKCi0gICBgZ2VvbV9lZGdlX2FyY2AgLSB0ZWViIGthYXJlIHB1bmt0aWRlIHZhaGVsZQoKLSAgIGBnZW9tX2VkZ2VfZGVuc2l0eWAgLSBqb29uZSBhc2VtZWwgdGVlYiBrYWhlIHB1bmt0aSB2YWhlbGlzdCBhbGEgbmF0dWtlIHR1bWVkYW1ha3MsIGthc3VsaWssIGt1aSBncmFhZiBvbiB2w6RnYSBzdXVyIGphIHRpaGUKCmBgYHtyfQpnZ3JhcGgoZywgbGF5b3V0ID0gImxpbmVhciIpICsKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgZ2VvbV9lZGdlX2FyYygpCgpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICBnZW9tX2VkZ2VfZGVuc2l0eSgpCmBgYAoKS3VpIGdyYWFmaWQgb24gc3V1bmF0dWQgc2FhYiBqb29udGVsZSBsaXNhZGEga2Egbm9vbGksIG1pZGEgc2FhYiBzcGV0c2lmaXRzZWVyaWRhIGFyZ3VtZW5kaWdhIGBhcnJvd2AsIG1pcyBvbWFta29yZGEgdsO1dGFiIHbDpMOkcnR1c2VrcyBgYXJyb3dgIGZ1bmt0c2lvb25pIGBncmlkYCBwYWtldGlzdC4KCmBgYHtyfQpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICBnZW9tX2VkZ2VfbGluayhhcnJvdyA9IGdyaWQ6OmFycm93KGxlbmd0aCA9IHVuaXQoMC4xLCAiaW5jaGVzIiksIHR5cGUgPSAiY2xvc2VkIikpICNjbG9zZWQgamEgb3BlbiB0w6RoZW5kYXZhZCBub29sZSB0w7zDvHBpCmBgYAoKIyMjIEdyYWFmaWwgbWV0YS1hbmRtZXRlIG7DpGl0YW1pbmUKClNpaWFuaSBuw6RpZGF0dWQgZ3JhYWZpa3VkIG9uIG9sbnVkIHbDpGdhIG11c3R2YWxnZWQgbmluZyBrb2hhdGkgb24gcmFza2UgZXJpc3RhZGEgdGlwcGUgamEgc2VydmFzaWQuIFbDpHJ2aWRlIGphIG11dWRlIHBhcmFtZWV0cml0ZSBsaXNhbWluZSBrw6RpYiBzaXN1bGlzZWx0IHNhbWEgbW9vZGkga3VpIGdncGxvdDItcy4gU8O1bHR1dmFsdCBzZWxsZXN0IGthcyBwYXJhbWVldGVyIG7DpGl0YWIgbcO1bmUgdHVubnVzZSB2w6TDpHJ0dXNpIHbDtWkgb24gaWx1IHDDpHJhc3QsIGzDpGhlYiB0YSBrYXMgZnVua3RzaW9vbmkgYWVzIHNpc3NlIHbDtWkgasOkw6RiIHNlbGxlc3QgdsOkbGphLiBQcm9vdmltZSB2w6RydmkgYXJndW1lbmRpZ2EuCgpgYGB7cn0KI1NpaW4gYW5uYW1lIGxpaHRzYWx0IHbDpHJ2aSB0aXBwdWRlbGUKZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAicmVkIikgKwogIGdlb21fZWRnZV9saW5rKCkKCiNTaWluIHByb292aW1lIHB1bmt0aWRlIHR1bWVkdXNlZ2EgaW5mb3QgZWRhc2kgYW5kYQpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBBcnRpc3RQb3B1bGFyaXR5KSkgKwogIGdlb21fZWRnZV9saW5rKCkKYGBgCgpQYW5lbWUgdMOkaGVsZSwgZXQgam9vbmVkIHTDtW1tYXRha3NlIHB1bmt0aWRlIHBlYWxlLCBzZXN0IG5lbmRlIGpvb25pc3RhbWlzZSBrw6RzayB0dWxpIHDDpHJhc3QgdGlwcHVkZSBvbWEuIFNlZXTDtXR0dSBvbiB0YXJnZW0gasOkcmpla29yZGEgbXV1dGEuCgpgYGB7cn0KZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluaygpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gQXJ0aXN0UG9wdWxhcml0eSkpIApgYGAKClBlYW1pc2VkIHBhcmFtZWV0cmlkIG1pZGEgbXV1dGEgb24gYGNvbG9yYCwgYHNoYXBlYCAocHVua3RpZGVsKSwgYHNpemVgIChwdW5rdGlkZWwpLCBgd2lkdGhgIChqb29udGVsKSBqYSBgbGluZXR5cGVgIChqb29udGVsKS4KCmBgYHtyfQpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFlcyhjb2xvciA9IG5UcmFja3MpKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gQXJ0aXN0Rm9sbG93ZXJzKSwgY29sb3IgPSAiZGFya2dyZWVuIikgCmBgYAoKR3JhYWZpbGlzdGUgcGFyYW1lZXRyaXRlIHNrYWxlZXJpbWlzZWtzIG9uIGxvb2R1ZCBlcmFuZGkgZnVua3RzaW9vbmlkLCBldCBuw6RpdGVrcyB0aXBwdWRlIGphIHNlcnZhZGUgdsOkcnZlIHNhYWtzIGVyYWxkaSBza2FsZWVyaWRhLiBUaXBwdWRlIHB1aHVsIHTDtsO2dGF2YWQgdGF2YWxpc2VkIGBzY2FsZV8qYCBmdW5rdHNpb29uaWQgYWdhIHNlcnZhZGVsZSBvbiBuw6RpdGVrcyBmdW5rdHNpb29uaWQga3VqdWwgYHNjYWxlX2VkZ2VfKmAuIFNlbGxlZ2kgcG9vbGVzdCB0w7bDtnRhdmFkIG5lZWQgc2FtYSBtb29kaSBrdWkgdGF2YWxpc2VkIGBnZ3Bsb3QyYCB2YXJpYW5kaWQuCgpgYGB7cn0KZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMoY29sb3IgPSBuVHJhY2tzKSkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IEFydGlzdEZvbGxvd2VycyksIGNvbG9yID0gImRhcmtncmVlbiIpICsKICBzY2FsZV9lZGdlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJwaW5rIiwgaGlnaCA9ICJyZWQiKQpgYGAKCkVyaXRpIHZhamFsaWsgb24gc2thbGVlcmltaW5lIHNlcnZhIHBha3N1c2Uga29udHJvbGxpbWlzZWtzIGFyZ3VtZW5kaWdhIHdpZHRoLCBtaWxsZSB2YWlraW1pc2kgdsOkw6RydHVzZWQgb24gc3VodGVsaXNlbHQganViZWRhZC4KCmBgYHtyfQpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFlcyhjb2xvciA9IG5UcmFja3MsIHdpZHRoID0gbWF4UG9wdWxhcml0eSkpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSBBcnRpc3RGb2xsb3dlcnMpLCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgc2NhbGVfZWRnZV9jb2xvcl9ncmFkaWVudChsb3cgPSAicGluayIsIGhpZ2ggPSAicmVkIikgCgpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFlcyhjb2xvciA9IG5UcmFja3MsIHdpZHRoID0gbWF4UG9wdWxhcml0eSkpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSBBcnRpc3RGb2xsb3dlcnMpLCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgc2NhbGVfZWRnZV9jb2xvcl9ncmFkaWVudChsb3cgPSAicGluayIsIGhpZ2ggPSAicmVkIikgKwogIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAsIDIpKQpgYGAKCiMjIyBUZWtzdGlkZSBsaXNhbWluZSBncmFhZmlsZQoKU2lpYW1hYW5pIG9sZW1lIGpvb25pc3RhbnVkIGvDvGxsIHB1bmt0aWtlc2ksIGt1aWQgbmVuZGUgcHVua3RpZGUgdGVnZWxpa2t1IGlkZW50aXRlZXRpIHbDtWltZSB2YWlkIGFpbWF0YS4gRXQgcGFyZW1pbmkgdGVhZGEgc2FhZGEsIG1pcyB0aXBwdWRlZ2Egb24gdMOkcHNlbWFsdCB0ZWd1LiBzYWFtZSBsaXNhZGEgdGlwcHVkZWxlIG5pbWVkLiBTZWRhIHNhYWIgdGVoYSBuaWkgZnVua3RzaW9vbmlnYSBgZ2VvbV9ub2RlX3RleHRgLCBrdWkgYGdlb21fbm9kZV9sYWJlbGAsIG1pcyB2YXN0YXZhbHQgam9vbmlzdGF2YWQgbGlodHNhbHQgdGVrc3RpIHbDtWkgc2lpcyBrYXN0aWtlc2VnYSBzaWxkaS4KCmBgYHtyfQpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFlcyh3aWR0aCA9IG1heFBvcHVsYXJpdHkpLCBjb2xvciA9ICJncmV5NjAiKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gQXJ0aXN0Rm9sbG93ZXJzKSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAsIDIpKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gQXJ0aXN0TmFtZSkpCgpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFlcyh3aWR0aCA9IG1heFBvcHVsYXJpdHkpLCBjb2xvciA9ICJncmV5NjAiKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gQXJ0aXN0Rm9sbG93ZXJzKSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAsIDIpKSArCiAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhsYWJlbCA9IEFydGlzdE5hbWUpKQpgYGAKCk7DvMO8ZCBvbiBhZ2EgcHJvYmxlZW0sIGV0IG1lIGVpIG7DpGUgZW5hbSB0aXBwdXNpZCAsIHNlc3QgbmVlZCBvbiB0ZWtzdGlkZSB0YWdhIHBlaWR1cy4gw5xsZcO8bGRzZSBvbiB0ZWtzdGUgbmlpIHBhbGp1LCBldCBuYWQgZWkgbWFodSBow6RzdGkgZWtyYWFuaWxlLiBLYXN1bGlrIG9sZWtzIG5laWQga3VpZGFnaSB2w6RoZW5kYWRhLiDDnGtzIHbDtWltYWx1cyBvbiBuw6RpZGF0YSBhaW51bHQgbWluZ2lzIG3DtXR0ZXMgaHV2aXRhdmFtYWlkIHRla3N0ZSwgbsOkaXRla3MgbmVpZCBhcnRpc3RlIGtlbGxlbCBvbiBwYWxqdSBqw6RsZ2lqYWlkLiBTZWxsZWtzIHNhYW1lIGthc3V0YWRhIGBnZW9tXypgIGFyZ3VtZW50aSBgZGF0YWAsIG1pbGxsZWxlIG1lIHNhYW1lIGFuZGEgZXR0ZSBmdW5rdHNpb29uaSBtaXMgbW9kaWZpdHNlZXJpYiBzZWxsZSBraWhpIGFuZG1laWQuCgpgYGB7cn0KZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSBtYXhQb3B1bGFyaXR5KSwgY29sb3IgPSAiZ3JleTYwIikgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IEFydGlzdEZvbGxvd2VycyksIGNvbG9yID0gImRhcmtncmVlbiIpICsKICBzY2FsZV9lZGdlX3dpZHRoKHJhbmdlID0gYygwLCAyKSkgKwogIGdlb21fbm9kZV9sYWJlbChhZXMobGFiZWwgPSBBcnRpc3ROYW1lKSwgZGF0YSA9IGZ1bmN0aW9uKC54KXsueCAlPiUgZmlsdGVyKEFydGlzdEZvbGxvd2VycyA+IDEwMDAwKX0pCmBgYAoKUGFuZSB0w6RoZWxlLCBldCBzZWUgZnVua3RzaW9vbiB0w7bDtnRhYiBhaW51bHQgdGlwcHVkZSBhbmRtZXRhYmVsaWwsIHNlc3Qgc2VlIG9uIHNlZSBtaWxsZSBgZ2VvbV9ub2RlX2xhYmVsYCBldHRlIHNhYWIuIEV0IHRyw7xra2ltaXNlIHZhZXZhIHbDpGhlbmRhZGEgc2FhYiBgZnVuY3Rpb24oLngpey54ICU+JSBmaWx0ZXIoQXJ0aXN0Rm9sbG93ZXJzID4gMTAwMDApfWAgdsOkbGplbmRhZGEgbMO8aGVtYWx0IGt1aSBgfiAueCAlPiUgZmlsdGVyKEFydGlzdEZvbGxvd2VycyA+IDEwMDAwKWAuIFNlbGxpc3Qga3VqdSBrYXN1dGF0YWtzZSBwYWxqdSBrYSB0aWR5dmVyc2UgcGFrZXRpcyBgcHVycnJgLCBtaXMgb24gbcO1ZWxkdWQgdmVrdG9yaXRlIGphIGxpc3RpZGUgdMO2w7Z0bGVtaXNla3MuCgpgYGB7cn0KZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSBtYXhQb3B1bGFyaXR5KSwgY29sb3IgPSAiZ3JleTYwIikgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IEFydGlzdEZvbGxvd2VycyksIGNvbG9yID0gImRhcmtncmVlbiIpICsKICBzY2FsZV9lZGdlX3dpZHRoKHJhbmdlID0gYygwLCAyKSkgKwogIGdlb21fbm9kZV9sYWJlbChhZXMobGFiZWwgPSBBcnRpc3ROYW1lKSwgZGF0YSA9IH4gLnggJT4lIGZpbHRlcihBcnRpc3RGb2xsb3dlcnMgPiAxMDAwMCkpCmBgYAoKU2lpc2tpIG9uIGtvbmtyZWV0c2VkIHRpcHVkIHNpbHRpZGUgdmFyanVzIGphIGt1aSBzaWx0ZSBvbGVrcyByb2hrZW0sIHNpaXMga2F0dHVrc2lkIG5lZWQga2Egw7xsZS4gRXQgcHJvb3ZpZGEgbGVpZGEgc2lsdGlkZWxlIGhlYWQgbWl0dGUtw7xsZWthdHR1dmFkIHBvc2l0c2lvb25pZCB2w7VpYiByYWtlbmRhZGEgYXJndW1lbnRpIHJlcGVsLCBtaXMgcHJvb3ZpYiBvcHRpbWVlcmlkYSBzaWx0aWRlIGFzdWtvaHRpLgoKYGBge3J9CmdncmFwaChnLCBsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gbWF4UG9wdWxhcml0eSksIGNvbG9yID0gImdyZXk2MCIpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSBBcnRpc3RGb2xsb3dlcnMpLCBjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IGMoMCwgMikpICsKICBnZW9tX25vZGVfbGFiZWwoYWVzKGxhYmVsID0gQXJ0aXN0TmFtZSksIGRhdGEgPSB+IC54ICU+JSBmaWx0ZXIoQXJ0aXN0Rm9sbG93ZXJzID4gNTAwMCkpCgpnZ3JhcGgoZywgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKGFlcyh3aWR0aCA9IG1heFBvcHVsYXJpdHkpLCBjb2xvciA9ICJncmV5NjAiKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gQXJ0aXN0Rm9sbG93ZXJzKSwgY29sb3IgPSAiZGFya2dyZWVuIikgKwogIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAsIDIpKSArCiAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhsYWJlbCA9IEFydGlzdE5hbWUpLCByZXBlbCA9IFQsIGRhdGEgPSB+IC54ICU+JSBmaWx0ZXIoQXJ0aXN0Rm9sbG93ZXJzID4gNTAwMCkpCmBgYAoKTMO1cHVrcyB2YWF0YW1lIGdyYWFmaSBrdWp1bmR1c2VsZW1lbnRpZGUgbXV1dG1pc3QuIFNpaW4gdMO2w7Z0YXZhZCB0ZWdlbGlrdWx0IGvDtWlrIGBnZ3Bsb3QyYCBgdGhlbWVfKmBmdW5rdHNpb29uaWQgYWdhIGt1bmEgbmVpbCBvbiBzdXVyIHLDvGhrIHRlbGdlZGUga3VqdXRhbWlzZWwgbmluZyBraXJqZWxkYW1pc2VsLCBzaWlzIGdyYWFmaWRlIGphb2tzIG5hZCBsaWlnYSBoZWFkIGVpIG9sZS4gUGFyZW0gb24ga2FzdXRhZGEgYHRoZW1lX3ZvaWRgIHbDtWkgYHRoZW1lX2dyYXBoYC4gS29ua3JlZXRzZXRlIGVsZW1lbnRpZGUgbXV1dG1pc2VrcyBzYWFiIGthc3V0YWRhIGB0aGVtZWAgZnVua3RzaW9vbmkuCgpgYGB7cn0KZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSBtYXhQb3B1bGFyaXR5KSwgY29sb3IgPSAiZ3JleTYwIikgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IEFydGlzdEZvbGxvd2VycyksIGNvbG9yID0gImRhcmtncmVlbiIpICsKICBzY2FsZV9lZGdlX3dpZHRoKHJhbmdlID0gYygwLCAyKSkgKwogIGdlb21fbm9kZV9sYWJlbChhZXMobGFiZWwgPSBBcnRpc3ROYW1lKSwgcmVwZWwgPSBULCBkYXRhID0gfiAueCAlPiUgZmlsdGVyKEFydGlzdEZvbGxvd2VycyA+IDUwMDApKSArCiAgdGhlbWVfYncoKQoKZ2dyYXBoKGcsIGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSBtYXhQb3B1bGFyaXR5KSwgY29sb3IgPSAiZ3JleTYwIikgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IEFydGlzdEZvbGxvd2VycyksIGNvbG9yID0gImRhcmtncmVlbiIpICsKICBzY2FsZV9lZGdlX3dpZHRoKHJhbmdlID0gYygwLCAyKSkgKwogIGdlb21fbm9kZV9sYWJlbChhZXMobGFiZWwgPSBBcnRpc3ROYW1lKSwgcmVwZWwgPSBULCBkYXRhID0gfiAueCAlPiUgZmlsdGVyKEFydGlzdEZvbGxvd2VycyA+IDUwMDApKSArCiAgdGhlbWVfdm9pZCgpCmBgYAoKIyMjIyAgw5xsZXNhbmRlZAoKLSAgIFByb292aSBzYWF2dXRhZGEgasOkcmdtaW5lIHBpbHQuCgohW10oaW1hZ2VzL1NjcmVlbnNob3QlMjAyMDIxLTAzLTE0JTIwYXQlMjAyMy41My4yNi5wbmcpCgpgYGB7cn0KZ2dyYXBoKGcsIGxheW91dCA9ICJjaXJjbGUiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGNvbG9yID0gbWF4UG9wdWxhcml0eSkpKwogIHNjYWxlX2VkZ2VfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGMobG93ID0gIndoaXRlIiwgbWlkPSJyZWQiLCBoaWdoID0gImRhcmtibHVlIikpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yPUFydGlzdEdlbnJlKSkgKwogIGdlb21fbm9kZV9sYWJlbChhZXMobGFiZWwgPSBBcnRpc3RHZW5yZSksIGRhdGEgPSBmdW5jdGlvbigueCl7LnggJT4lIGZpbHRlcihBcnRpc3RHZW5yZSA9PSAiZXN0b25pYW4gaW5kaWUiKX0pCiAgCmBgYAoKCiMjIEdyYWFmaSBhbGdvcml0bWlkCgpHcmFhZmkgc3RydWt0dXVyaXN0IG9uIHbDtWltYWxpayBqw6RyZWxkYWRhIHDDpHJpcyBwYWxqdSBodXZpdGF2YXQgbWV0YWluZm90LCBzZWxsZSBpbmZvIGthc3V0YW1pbmUgdsO1aW1hbGRhYiBrYSBncmFhZmUgdmlzdWFhbHNlbHQgc2lzdWthbWFsdCBrdWp1dGFkYS4gU2VsbGVrcyB0dWxlYiBhZ2EgdGVhZGEga3VpZGFzIGVyaW5ldmFpZCBncmFhZmkgYWxnb3JpdG1lIHByYWt0aWthcyByYWtlbmRhZGEuCgpLw7VpZ2UgbGlodHNhbSBvbWFkdXMgbWlsbGUgbWUgZ3JhYWYgc3RydWt0dXVyaXN0IHbDpGxqYSBzYWFtZSBsdWdlZGEgb24gc2VlLCBrYXMgdGlwcCBvbiDDvGhlbmRhdHVkIHRlaXN0ZSB0aXBwdWRlZ2EgdsO1aSBtaXR0ZS4gU2VsbGVrcyBvbiBmdW5rdHNpb29uIGBub2RlX2lzX2lzb2xhdGVkYCBtaWRhIHNhYWIgc2lpcyByYWtlbmRhZGEga8Okc2t1ZGUgYG11dGF0ZWAgdsO1aSBgZmlsdGVyYCBzZWVzLiBQYW5lIHTDpGhlbGUsIGV0IHNlbGxlIGvDpHN1IHTDtsO2dGFtaXNla3MgcGVhdmFkIG9sZW1hIGFrdGl2ZWVyaXR1ZCB0aXB1ZC4gVGlwcHVkZWwgb24ga2EgcGFsanUgdGVpc2kgb21hZHVzaSBtaWRhIHNhYWIgdMOkcHNlbWFsdCB1dXJpZGEgYWJpZmFpbGlkZXN0IGA/bm9kZV9pc19pc29sYXRlZGAuCgpgYGB7cn0KI1NlbGxla3MsIGV0IHBhcmVtaW5pIG9sZWtzIG7DpGhhIMO8aGVuZGF0dWQgdGlwdWQKZyAlPiUgCiAgYWN0aXZhdGUobm9kZXMpICU+JSAKICBtdXRhdGUoSXNvbGF0ZWQgPSBub2RlX2lzX2lzb2xhdGVkKCkpICU+JSAKICBnZ3JhcGgobGF5b3V0ID0gImtrIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoKSArCiAgICBnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSBJc29sYXRlZCksIGNvbG9yID0gImRhcmtncmVlbiIpICsKICAgIHNjYWxlX3NpemVfZGlzY3JldGUocmFuZ2UgPSBjKDMsIDEpKQoKI1ZhYXRhbWUgYWludWx0IG5laWQgdGlwcGUsIG1pcyBlaSBvbGUgaXNvbGVlcml0dWQgKGlzb2xlZXJpdHVkIG1laWQgZWkgaHV2aXRhIG5hZ3VuaWkpCmcgJT4lIAogIGFjdGl2YXRlKG5vZGVzKSAlPiUgCiAgZmlsdGVyKCFub2RlX2lzX2lzb2xhdGVkKCkpICU+JSAKICBnZ3JhcGgobGF5b3V0ID0gImtrIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoKSArCiAgICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAiZGFya2dyZWVuIikKYGBgCgpKw6RyZ21pc2VuIHZhYXRhbWUgdGlwcHVkZSBzZW9zZWlkIGtvbmtyZWV0c2V0ZSB0aXBwdWRlZ2EuIEthdWd1c3QgdGlwcHVkZWdhIHNhYWIgbGVpZGEga8Okc3VnYSBgbm9kZV9kaXN0YW5jZV90b2AgamEgYG5vZGVfZGlzdGFuY2VfZnJvbWAuIFN1dW5hbWF0YSBncmFhZmkgcHVodWwgcG9sZSBzdXVydCB2YWhldCBrdW1iYSBrYXN1dGFkYSwga3VpZCBzdXVuYXR1ZCBncmFhZmkgcHVodWwgdHVsZWIgZXJpbmV2dXMgc2lzc2UuIE5lbmRlIGvDpHNrdWRlZ2Egb24gdsOkZ2EgaGVhIGVyYWxkYWRhIHbDpGxqYSBhbGFtZ3JhYWZlLCBtaXMga2Vza2VuZHV2YWQgw7xoZSBwdW5rdGkgw7xtYnJ1c2VsZS4gT24ga2Ega2VlcnVrYW1haWQgdGlwcHVkZSB2YWhlbGlzaSBzdWh0ZWlkIHbDtWltYWxpayBhcnZ1dGFkYSBqYSBuZWlkIG7DpGViIGFiaWxlaGVsIGA/bm9kZV9kaXN0YW5jZV90b2AuCgpgYGB7cn0KI1ZhYXRhbWUgTnVibHUga2F1Z3VzZWlkIGphIGZpbHRyZWVyaW1lIHbDpGxqYSBuZWVkLCBrZXMgb24gTnVibHVzdCAyIHbDtWkgdsOkaGVtYSBzYW1tdSBrYXVndXNlbApnICU+JSAKICBhY3RpdmF0ZShub2RlcykgJT4lIAogIG11dGF0ZShkTnVibHUgPSBub2RlX2Rpc3RhbmNlX3RvKEFydGlzdE5hbWUgPT0gIk51Ymx1IikpICU+JSAKICBmaWx0ZXIoZE51Ymx1IDwgMykgJT4lIAogIGdncmFwaChsYXlvdXQgPSAia2siKSArCiAgICBnZW9tX2VkZ2VfbGluaygpICsKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgICBnZW9tX25vZGVfbGFiZWwoYWVzKGxhYmVsID0gQXJ0aXN0TmFtZSksIHJlcGVsID0gVCwgZGF0YSA9IH4gLnggJT4lIGZpbHRlcihBcnRpc3ROYW1lID09ICJOdWJsdSIpKQpgYGAKClRpcHUgb2x1bGlzdXN0IGdyYWFmaXMgdsO1aWIgbsOkaWRhdGEgbWl0dXQgbW9vZGksIGt1aWQga8O1aWtpIG5laWQgbMOkaGVuZW1pc2kgdsO1ZXRha3NlIGtva2t1IMO8aGUgdGVybWluaWdhIHRzZW50cmFhbHN1cy4gS8O1aWcgbGlodHNhbSB0c2VudHJhYWxzdXNlIG3DtcO1dCwgdGlwdSB2YWxlbnRzLCBsb2ViIGtva2t1IGvDtWlrIHRpcHUgw7xoZW5kdXNlZC4gU3V1bmF0dWQgZ3JhYWZpcyB2w7VpYiBzZWRhIHRlaGEgbmlpIHNpc3NlIGt1aSB2w6RsamFwb29sZSDDvGhlbmR1c3RlZ2EgZXJhbGRpLiBSLXMgc2FhYiBzZWRhIGFydnV0YWRhIGvDpHN1Z2EgYGNlbnRyYWxpdHlfZGVncmVlYC4gS2VlcnVrYW0gdHNlbnRyYWFsc3VzbcO1w7V0IG9uICpiZXR3ZWVubmVzcyBjZW50cmFsaXR5KiwgbWlzIG3DvMO8ZGFiIGt1aSBzdXVyIG9zYSDDvGhlbmR1c3Rlc3QgZ3JhYWZpIHRpcHB1ZGUgdmFoZWwgbMOkYmliIHZhc3RhdmF0IHRpcHB1LiBFcmluZXZhaWQgYWxnb3JpdG1lIG9uIGFnYSB2ZWVsICh2dCBgP2NlbnRyYWxpdHlfZGVncmVlKWAKCmBgYHtyfQojVmFhdGFtZSBwdW5rdGlkZSBjZW50cmFsaXR5dCBqYSBkZWdyZWVkIHN1dXJ1c3RlIGphIHbDpHJ2aWRlIGFiaWdhIGphIGZpbHRyZWVyaW1lIHbDpGxqYSBuZWVkLCBrdXMgY2VudHJhbGl0eSBvbiBzdXVyZW0ga3VpIDEwCmcgJT4lIAogIGFjdGl2YXRlKG5vZGVzKSAlPiUgCiAgbXV0YXRlKERlZ3JlZSA9IGNlbnRyYWxpdHlfZGVncmVlKCkpICU+JSAKICBtdXRhdGUoQ2VudHJhbGl0eSA9IGNlbnRyYWxpdHlfZGVncmVlKCkpICU+JSAKICBnZ3JhcGgobGF5b3V0ID0gImtrIikgKyAKICAgIGdlb21fZWRnZV9saW5rKCkgKyAKICAgIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IERlZ3JlZSwgY29sb3IgPSBDZW50cmFsaXR5KSkgKwogICAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhsYWJlbCA9IEFydGlzdE5hbWUpLCBkYXRhID0gfiAueCAlPiUgZmlsdGVyKENlbnRyYWxpdHkgPiAxMCkpCmBgYAoKT21hZHVzaSB2w7VpYiB2YWFkYXRhIGthIHNlcnZhZGVsLiBLYSBuZW5kZSBwdWh1bCBzYWFiIGxlaWRhIHNlcnZhZCwgbWlzIMO8aGVkdXZhZCBrb25rcmVldHNlIHRpcHVnYSB2w7VpIHNlcnZhc2lkIG1pcyBvbiB2w7VyZ3VzdGlrdXMga2Vza3NlbCBrb2hhbC4gTmVpZCBmdW5rdHNpb29uZSBzYWFiIGthc3V0YWRhIGvDpHNrdWRlZ2EgYGVkZ2VfaXNfZnJvbWAgamEgYGNlbnRyYWxpdHlfZWRnZV9iZXR3ZWVubmVzc2AuCgpgYGB7cn0KI0V0IHBhcmVtaW5pIG7DpGhhIHNlcnZhc2lkLCBtaXMgw7xoZW5kdXZhZCBudWJsdWdhCmcgJT4lIAogIGFjdGl2YXRlKGVkZ2VzKSAlPiUgCiAgbXV0YXRlKGVOdWJsdSA9IGVkZ2VfaXNfZnJvbSgoZyAlPiUgYWN0aXZhdGUobm9kZXMpICU+JSBwdWxsKEFydGlzdE5hbWUpKSA9PSAiTnVibHUiKSkgJT4lIAogIGdncmFwaChsYXlvdXQgPSAia2siKSArIAogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gZU51Ymx1KSkgKyAKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgICBzY2FsZV9lZGdlX3dpZHRoX2Rpc2NyZXRlKHJhbmdlID0gYygwLjIsIDEpKQoKI1NlcnZhZGUgY2VudHJhbGl0eSBtw7XDtWR1IGt1anV0YW1pbmUKZyAlPiUgCiAgYWN0aXZhdGUoZWRnZXMpICU+JSAKICBtdXRhdGUoQ2VudHJhbGl0eSA9IGNlbnRyYWxpdHlfZWRnZV9iZXR3ZWVubmVzcygpKSAlPiUgCiAgZ2dyYXBoKGxheW91dCA9ICJrayIpICsgCiAgICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSBDZW50cmFsaXR5KSkgKyAKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJkYXJrZ3JlZW4iKSArCiAgICBzY2FsZV9lZGdlX3dpZHRoX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAuMiwgMS41KSkKYGBgCgpHcmVlZmkgdGlwcGUgb24ga2Ega2FzdWxpayBrbGFzdGVyZGFkYSwgZXQgw7xsZXMgbGVpZGEgbG9vZ2lsaXNlZCBhbGFtZ3JhYWZpZC4gTGlodHNhaW0ga2xhc3RlcmRhbWlzZSB2aWlzIG9uIGxlaWRhIHNpZHVzYWQgYWxhbWdyYWFmaWQsIG1pZGEgc2FhYiB0ZWhhIGvDpHN1Z2EgYGdyb3VwX2NvbXBvbmVudHNgLiBLZWVydWthbWFpZCBrYWxzdGVyZHVzaSBvbiBrYSBww6RyaXMgcGFsanUgc2FhZGF2YWwgamEgw7xoZSBzZWxsaXNlIG7DpGl0ZWtzIHbDtWlrcyBvbGxhIGBncm91cF9sb3V2YWluYCAodGVpc3RlIGphb2tzIHV1cmkgYD9ncm91cF9sb3V2YWluYCkuCgpgYGB7cn0KZyAlPiUgCiAgYWN0aXZhdGUobm9kZXMpICU+JSAKICBmaWx0ZXIoIW5vZGVfaXNfaXNvbGF0ZWQoKSkgJT4lIAogIG11dGF0ZShDQyA9IGdyb3VwX2NvbXBvbmVudHMoKSkgJT4lIAogIGdncmFwaChsYXlvdXQgPSAia2siKSArIAogICAgZ2VvbV9lZGdlX2xpbmsoKSArCiAgICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gQ0MpKSAgCgpzIDwtIGcgJT4lIAogIGFjdGl2YXRlKGVkZ2VzKSAlPiUgCiAgZmlsdGVyKGZyb20gPiAzICYgdG8gPiAzKQoKCgpnICU+JSAKICBhY3RpdmF0ZShub2RlcykgJT4lIAogIGZpbHRlcighbm9kZV9pc19pc29sYXRlZCgpKSAlPiUgCiAgbXV0YXRlKEdMID0gYXMuZmFjdG9yKGdyb3VwX2xvdXZhaW4oKSkpICU+JSAKICBnZ3JhcGgobGF5b3V0ID0gImtrIikgKyAKICAgIGdlb21fZWRnZV9saW5rKCkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvciA9IEdMKSkrCiAgICBnZW9tX25vZGVfbGFiZWwoYWVzKGxhYmVsID0gQXJ0aXN0TmFtZSkgZGF0YSA9ICkKCmBgYAoKIyMjIyDDnGxlc2FuZGVkCgotICAgVmlzdWFsaXNlZXJpIGFudHVkIGFuZG1lc3Rpa3Uga8O1aWdlIHN1dXJlbSBzaWR1cyBrb21wb25lbnQuIE7DpGl0YSBuaW1lZGVnYSBrw7VpayBhcnRpc3RpZCwga2VsbGVsIG9uIHJvaGtlbSBrdWkgMyBrb29zdMO2w7ZkLiBOw6RpdGEgcHVua3RpIHTDvMO8YmlnYSDDpHJhIHNlbGxlIHNlbGxlcyBncmFhZmlzIG9sZXZhZCBrbGFzdGlyaWQgKExvdXZhaW4taSBhbGdvcml0bWkgasOkcmdpKQoKYGBge3J9CmcgJT4lIAogIGFjdGl2YXRlKG5vZGVzKSAlPiUKICBtdXRhdGUoQ0MgPSBncm91cF9jb21wb25lbnRzKCkpICU+JSAKICBmaWx0ZXIoQ0M9PTEpICU+JSAKICBtdXRhdGUoRGVncmVlID0gY2VudHJhbGl0eV9kZWdyZWUoKSkgJT4lIAogIG11dGF0ZShHTCA9IGFzLmZhY3Rvcihncm91cF9sb3V2YWluKCkpKSAlPiUgCiAgZ2dyYXBoKGxheW91dCA9ICJrayIpICsgCiAgICBnZW9tX2VkZ2VfbGluaygpICsKICAgIGdlb21fbm9kZV9wb2ludChhZXMoc2hhcGU9R0wpKSsKICAgIGdlb21fbm9kZV9sYWJlbChhZXMobGFiZWwgPSBBcnRpc3ROYW1lKSwgZGF0YSA9IH4gLnggJT4lIGZpbHRlcihEZWdyZWUgPiAzKSkKYGBgCgoKIyMgS29kdW5lIMO8bGVzYW5uZQoKRmFpbGlzIGBzcG90aWZ5X2ZvcmVpZ24uUkRhdGFgIG9uIHNhbWFzdWd1c2VkIGFuZG1lZCBhZ2EgdsOkbGlzYXJ0aXN0aWRlIGtvaHRhLiBQcm9vdmkgc2VhbHQgdsOkbGphIHR1dWEgbcO1bmkgc2ludSBhcnZhdGVzIGh1dml0YXYgc2VvcyDDvGhlIHZpc3VhbGlzYXRzaW9vbmlnYS4gRXNpdGFkYSB0dWxla3MgbmlpIGdyYWFmaSBnZW5lcmVlcml2IGtvb2Qga3VpIGthIHNlbGdpdHVzLCBtaWRhIHRlIHBpbGRpbHQgdsOkbGphIGx1Z2VzaXRlLiBQaWxkaSBwdWh1bCBoaW5kYW4sIG1pbGxpc2VpZCB2w7V0dGVpZCB0ZSBvbGV0ZSBrYXN1dGFudWQgcGlsZGkgZXR0ZXZhbG1pc3RhbWlzZWwsIGthc3V0YXR1ZCB2w7Vpa3Mgb2xsYSB2w6RoZW1hbHQgw7xrcyBlbGVtZW50IGvDtWlnZXN0IGrDpHJnbmV2YXN0LgoKLSAgIE9uIGxlaXR1ZCBzb2JpdiBwdW5rdGlkZSBwYWlndXR1cwoKLSAgIE9uIG11dWRldHVkIHRpcHB1ZGUgZ3JhYWZpbGlzaSBvbWFkdXNpCgotICAgT24gbXV1ZGV0dWQgc2VydmFkZSBncmFhZmlsaXNpIG9tYWR1c2kKCi0gICBPbiBuw6RpZGF0dWQgw6RyYSBodXZpcGFra3V2YW1hZCB0aXB1ZCBuaW1lIHbDtWkgbcO1bmUgbXV1IHRla3N0aWdhCgotICAgT24ga2FzdXRhdHVkIGdyYWFmaSBhbGdvcml0bWUsIGFuZG1ldGUgZWVsdMO2w7Z0bHVzZWtzIHbDtWkgZ3JhYWZpbGlzdGUgZWxlbWVudGlkZSBsaXNhbWlzZWtzCgpMaXNha3MgdGVobmlsaXNlbGUgcG9vbGVsZSBoaW5kYW4gasOkcmVsZHVzZSBqYSBncmFhZmlrdSBrb2trdWxhbmdldnVzdCBuaW5nIGdyYWFmaWt1IHZpc3VhYWxzZXQgdm9ybWlzdHVzdC4K